(* The class File+ provides (among many other things) an interface with the
file system that simplifies data transfer in accordance with the user
interface guidelines. Thus we provide methods %OPEN:, %SAVE: and %SAVEAS:.
We rather arbitrarily use "%" before the name as Open: is needed for the
superclass File.
Our basic strategy is that %OPEN: opens the file, transfers all the file
data to an object in memory, then closes the file. Likewise %SAVE: and
%SAVEAS: transfer all the data to a file which is then closed, and the
volume is flushed as well. This strategy has a number of advantages.
It minimizes disk swapping if files on different disks are to be accessed.
It also gives us the best chance that the disk, and especially its
directory, will be valid in the event of a power failure or program crash.
Note also that RAM cache data is written to disk by a volume flush.
The disadvantage is that we must have enough memory to hold all of the
data of one or more files at once. This should not be a problem on a Mac
with 512K or greater, for normal file sizes. Of course this approach also
makes the processing of the data quicker and easier, in general.
The data movement is accomplished by sending a late-bound SEND: or
BRING: message to the passed-in object. This allows the object to
send out and bring in its data in whatever way it likes. Obviously a
class intended for use with FILE+ must support these methods. For some
classes, after BRING: a FIXUP: message must be sent to the object. This
allows any time-consuming reformatting to be deferred until the I/O
transfer is over, allows the possibility of it being run as a separate
task, and also allows an opportunity for BRING: to use an asynchronous
read.
For %OPEN:, %SAVE: and %SAVEAS:, the file may or may not already be open.
If the file is already open, it is reused rather than deleted - this way,
it stays in the same place in its folder.
*)
\ ======= Finder info class ========
\ With the advent of System 7 and AppleEvents, this class is no longer
\ relevant. However many applications will have to be able to run under
\ earlier systems. But if you try to call this class when AppleEvents are
\ available, you'll now get an error message.
:class FIN super{ object }
private
:m FINFO: \ ( -- addr )
AppleEvents? ?error 63 fInfo ;m
public
:m PRINT?: fInfo: self w@x ;m
:m SIZE: \ ( -- #files )
fInfo: self 2+ w@ ;m
:m FREC: \ ( idx -- addr )
fInfo: self 4+
swap ( idx ) 0 ?do 8 + dup c@ + 1+ align loop ;m
:m VREF: \ ( idx -- refNum )
fRec: self w@ ;m
:m FTYPE: \ ( idx -- fType )
fRec: self 2+ @ ;m
:m FVER: \ ( idx -- fVer )
fRec: self 6 + c@ ;m
:m FILENAME: \ ( idx -- addr len )
fRec: self 8 + count ;m
:m =: ( idx ) { fileObj -- }
dup filename: self name: [ fileObj ]
vRef: self setVref: [ fileObj ] ;m
;class
fin FINDERINFO
\ =============================
:class FILE+ super{ file }
:m GETCREATOR: \ ( -- crtr )
^base $ 24 + @ ;m
:m BEGIN:
\ Puts the current position and EOF of the file to the start. The file must already be open.
0 moveto: self OK?
0 ^base 28 + ! ^base fdos$ A012 OK? ;m
:m (%OPEN): { obj -- }
openReadOnly: self OK?
0 moveto: self OK? \ In case it was already open
watchCurs
^base bring: [ obj ] arrowCurs
close: self drop
\ Note: we ignore any error here since if we ran out of
\ memory, NOMEM may have closed the file already.
;m
:m (%SAVE): { obj -- }
watchCurs
begin: self ^base send: [ obj ] close: self OK?
flushVol: self
arrowCurs ;m
:m %OPEN: ( type1 ... typen n ) { obj -- b }
close: self drop \ just in case
stdGet: self dup 0EXIT \ out if "cancel" hit
obj (%open): self ;m
\ OPENFNDR: opens the Nth file indicated by the Finder info.
:m OPENFNDR: { idx obj -- }
idx ^base =: finderinfo
obj (%open): self ;m
:m %SAVE: { obj -- }
open: self drop
obj (%save): self ;m
:m CREATE&SAVE: ( typ crtr ) { obj -- }
create: self OK? set: self
obj (%save): self ;m
:m %SAVEAS: ( typ crtr addr1 len1 addr2 len2 ) { obj -- b }
close: self drop \ just in case
stdput: self
nif ( cancel hit ) 2drop false exit then
obj create&save: self true ;m
\ GETLINE: is designed to work in the same way as in class WordBatch, so that the caller doesn't have to worry about whether his input is coming from a single file or a batch of files. It reads the next line into the given string and returns TRUE, or, if end file has been reached, sets the length of the string to zero and returns FALSE.
:m GETLINE?: { str -- b }
10000 str setsize: string+ \ Max line length - change if nec
str all: string+ readLine: self
dup -39 =
if ( end file )
drop
bytesRead: self dup str setsize: string+
0<>
else
OK?
bytesRead: self 1- \ Adjust length to exclude any RET